package http

import (
	"bytes"
	"fmt"
	"strconv"

	"../utils"
)

// Response 网络请求响应类
type Response struct {
	status  uint16
	headers map[string]string
	body    bytes.Buffer
}

type ResponseBuilder struct {
	response *Response
}

const (
	success      = `success`
	x_message    = `x-api-message`
	x_cache      = `Cache-Control`
	x_power_by   = `X-Powered-By`
	content_type = `Content-type`
	content_lent = `Content-Length`
	cors         = `Access-Control-Allow-Origin`
	x_total      = `x-api-total`
	x_page       = `x-api-page`
	x_size       = `x-api-size`
	x_version    = `x-api-version`
	Version      = `pgo-0.1.0`
)

func SendResponse(data interface{}, message string, status uint16) *ResponseBuilder {
	return InitBuilder().Status(status).SetHeader(x_message, message).Body(nil).Append(data)
}

func Redirect(uri string) *Response {
	return InitBuilder().Status(302).SetHeader(`Location`, uri).Body(nil).Build()
}

func InitBuilder() *ResponseBuilder {
	builder := &ResponseBuilder{
		response: &Response{
			status: 200,
		},
	}

	return builder.SetHeader(x_power_by, `pachira.cc`).SetHeader(x_version, Version)
}

func (builder *ResponseBuilder) Status(status uint16) *ResponseBuilder {
	builder.response.status = status
	builder.response.headers[x_message] = `success`

	return builder
}

func (builder *ResponseBuilder) SetHeader(key, value string) *ResponseBuilder {
	builder.response.headers[key] = value
	return builder
}

func (builder *ResponseBuilder) SetCache(duration uint, etag string) *ResponseBuilder {
	if duration < 1 {
		builder.response.headers[x_cache] = `no-store, no-cache, must-revalidate`
	} else {
		now := utils.Nowtime()
		builder.response.headers[x_cache] = fmt.Sprintf("public, max-age=%d", duration)
		builder.response.headers["Pragma"] = "cache"
		builder.response.headers["Age"] = strconv.FormatUint(uint64(duration), 10)
		builder.response.headers["Date"] = utils.Date(`Mon, 02 Jan 2006 15:04:05 GMT`, now)
		builder.response.headers["Expires"] = utils.Date(`Mon, 02 Jan 2006 15:04:05 GMT`, now+int64(duration))
	}

	if etag != "" {
		builder.response.headers["ETag"] = fmt.Sprintf(`"%s"`, etag)
	}

	return builder
}

func (builder *ResponseBuilder) SetCors(origin string, domains ...string) *ResponseBuilder {
	if domains[0] == `*` {
		builder.response.headers[cors] = domains[0]
	} else {
		for _, domain := range domains {
			if domain == origin {
				builder.response.headers[cors] = origin
				break
			}
		}
	}

	return builder
}

func (builder *ResponseBuilder) ContentType(ct string, size uint64) *ResponseBuilder {
	builder.response.headers[content_type] = ct

	if size > 0 {
		builder.response.headers[content_lent] = strconv.FormatUint(size, 10)
	}

	return builder
}

func (builder *ResponseBuilder) Pagination(pagination *utils.Pagination) *ResponseBuilder {
	builder.response.headers[x_total] = strconv.FormatUint(uint64(pagination.Total), 10)
	builder.response.headers[x_page] = strconv.FormatUint(uint64(pagination.Page), 10)
	builder.response.headers[x_size] = strconv.FormatUint(uint64(pagination.Size), 10)

	return builder
}

func (builder *ResponseBuilder) Page(total uint, page uint, size uint) *ResponseBuilder {
	builder.response.headers[x_total] = strconv.FormatUint(uint64(total), 10)
	builder.response.headers[x_page] = strconv.FormatUint(uint64(page), 10)
	builder.response.headers[x_size] = strconv.FormatUint(uint64(size), 10)

	return builder
}

func (builder *ResponseBuilder) Body(body []byte) *ResponseBuilder {
	builder.response.body.Truncate(builder.response.body.Len())
	if body != nil && len(body) > 0 {
		builder.response.body.Write(body)
	}

	return builder
}

func (builder *ResponseBuilder) HTML(html string) *ResponseBuilder {
	builder.response.body.Truncate(builder.response.body.Len())
	if html != "" {
		builder.response.body.WriteString(html)
	}

	return builder
}

func (builder *ResponseBuilder) Append(data interface{}) *ResponseBuilder {
	switch data.(type) {
	case string:
		builder.response.body.WriteString(data.(string))
	case int:
		builder.response.body.WriteByte(byte(data.(byte)))
	case byte:
		builder.response.body.WriteByte(data.(byte))
	case []byte:
		builder.response.body.Write(data.([]byte))
	default:
	}

	return builder
}

func (builder *ResponseBuilder) Build() *Response {
	return builder.response
}
